#!/usr/bin/python3
# -*- coding: utf-8 -*-

from os import unlink
from os.path import isfile, isdir, dirname
from re import compile as compile_regex, UNICODE, DOTALL

from ikanda.core.credentials import Credential, PasswordCredential
from ikanda.core.envnames import *
from ikanda.core.callbacks import CallbackLogger
from ikanda.core.command import ensure_dir, Environment, DownloadCommand
from ikanda.core.local_commands import LocalExecuteCommand
from ikanda.core.proxy import create_proxy
from ikanda.core.validators import reject_if

_SUBVERSION_COMMON_ARGS = (
    '--non-interactive',
    '--trust-server-cert',
    '--force',
    '--config-option',
    'config:miscellany:use-commit-times=yes',
)

_SVN_UPDATE_REVISION = compile_regex(r'revision.+?(?P<number>\d+)', DOTALL | UNICODE)


class SubversionDownloadCommand(DownloadCommand):
    """ 使用 `Subversion` 下载软件包的命令。

    """
    __slots__ = ('__credential',)

    def __init__(self,
                 src: str,
                 dst: str,
                 credential: Credential,
                 description: str = '',
                 check_required: bool = False
                 ):
        """
        构造使用 `Subversion` 下载包的命令。

        :param src: 下载的源地址。
        :param dst: 下载的目标路径（本地路径）
        """
        super().__init__(src, dst, description, check_required)

        reject_if(lambda: not credential)

        self.__credential = credential

    @property
    def credential(self):
        return self.__credential

    def __call__(
            self,
            environment: Environment,
            resources: list,
            callback: CallbackLogger
    ):
        src = self.interpolate(self.src, environment)
        dst = self.abspath(self.interpolate(self.dst, environment), environment)
        project_name = environment.getstr(PROJECT_NAME)
        version_number = environment.getstr(PKG_VERSION_NUMBER)

        if isdir(dst):
            # 如果svn目录存在，那么执行update
            self.__do_update(dst, version_number, callback=callback, environment=environment, resources=resources)
        elif isfile(dst):
            # 如果存在同名文件，先删除此同名文件，然后执行checkout
            unlink(dst)
            self.__do_check_out(src, dst, version_number, callback=callback, environment=environment,
                                resources=resources)
        else:
            # 否则直接执行checkout
            ensure_dir(dirname(dst))
            self.__do_check_out(src, dst, version_number, callback=callback, environment=environment,
                                resources=resources)

        environment[PKG_DISPLAY_NAME] = project_name

    def __do_check_out(
            self,
            src: str,
            dst: str,
            version_number: str,
            environment: Environment,
            resources: list,
            callback: CallbackLogger
    ):
        args = ['co']
        if isinstance(self.credential, PasswordCredential):
            args += ['--username', self.credential.username, '--password', self.credential.password]
        else:
            raise RuntimeError('unsupported credential: ' + str(self.credential))

        args += [*_SUBVERSION_COMMON_ARGS]

        if version_number:
            args += ['-r', version_number]

        args += [src, dst]

        self.__call_svn(tuple(args), callback=callback, environment=environment, resources=resources)

    def __do_update(
            self,
            dst: str,
            version_number: str,
            environment: Environment,
            resources: list,
            callback: CallbackLogger
    ):
        args = ['up', *_SUBVERSION_COMMON_ARGS]

        if version_number:
            args += ['-r', version_number]

        args += [dst]

        self.__call_svn(tuple(args), callback=callback, environment=environment, resources=resources)

    def __call_svn(
            self,
            args: tuple,
            environment: Environment,
            resources: list,
            callback: CallbackLogger
    ):
        def call_verbose(callee):
            if callee.name_is('verbose'):
                m = _SVN_UPDATE_REVISION.search(callee.getarg(0))
                if m:
                    environment[PKG_VERSION_NUMBER] = m.group('number')
            else:
                return callee.execute()

        line = ' '.join(('svn', *args,))
        cmd = LocalExecuteCommand(command_lines=(line,), description='call subversion client',
                                  check_required=self.check_required)
        callback = create_proxy(callback, call_verbose)
        cmd(callback=callback, environment=environment, resources=resources)
